App.require(["matrix3", "canvasTool"], function (matrix3, canvasTool) {
    var Matrix3 = matrix3.Matrix3;
    App.defineWidget("path", {
        render: function (ctx) {
            ctx.save();
            var style = this.style;
            ctx.globalAlpha = style.opacity;
            var shadowObj = App.Widget.strToShadow(style.shadow);
            if (shadowObj) {
                ctx.shadowOffsetX = shadowObj.x;
                ctx.shadowOffsetY = shadowObj.y;
                ctx.shadowBlur = shadowObj.blur;
                ctx.shadowColor = shadowObj.color;
            }
            var transforms = App.Widget.strToTransform(style.transform);
            if (transforms) {
                for (var i = 0; i < transforms.length; i++) {
                    var tranObj = transforms[i];
                    ctx[tranObj.key].apply(ctx, tranObj.param);
                }
            }
            ctx.translate(style.x, style.y);
            this.createPath(ctx);
            if (style.fill) {
                ctx.fillStyle = style.fill;
                ctx.fill();
            }
            if (ctx.stroke) {
                if (Array.isArray(style.linedash) && style.linedash.length > 0) {
                    ctx.setLineDash(style.linedash);
                    ctx.lineDashOffset = style.linedashoffset;
                }
                ctx.strokeStyle = style.stroke;
                ctx.linewidth = style.linewidth;
                ctx.stroke();
            }
            ctx.restore();
        },
        isPointIn: function (ctx, x, y) {
            this.createPath(ctx);
            return ctx.isPointInPath(x, y);
        },
        initialize: function () {
            var style = this.style;
            var obj = canvasTool.changePath(style.path);
            this.action = obj.action;
            this.points = obj.points;
        },
        action: [],
        points: [],
        style: {
            transform: [], 
            x: 0,
            y: 0,
            path: "M 0 0 50 0 30 40Z",
            shadow: "0 0 0 black",
            stroke: "black",
            fill: "red",
            opacity: 1,
            linewidth: 1,
            marker: "",
            linedash: "",
            linecap: "",
            linejoin: "",
            linedashoffset: 0
        },
        get x() {
            return this.style.x;
        },
        set x(val) {
            this.style.x = val;
            this.getScene().nextRender();
        },
        get y() {
            return this.style.y;
        },
        set y(val) {
            this.style.y = val;
            this.getScene().nextRender();
        },
        get transform() {
            return this.style.transform;
        },
        set transform(val) {
            this.style.transform = val;
            this.getScene().nextRender();
        },
        get path() {
            return this.style.path;
        },
        set path(val) {
            this.style.path = val;
            this.getScene().nextRender();
        },
        set shadow(val) {
            this.style.shadow = val;
            this.getScene().nextRender();
        },
        get shadow() {
            return this.style.shadow;
        },
        get stroke() {
            return this.style.stroke;
        },
        set stroke(val) {
            this.style.stroke = val;
            this.getScene().nextRender();
        },
        get fill() {
            return this.style.fill;
        },
        set fill(val) {
            this.style.fill = val;
            this.getScene().nextRender();
        },
        get opacity() {
            return this.style.opacity;
        },
        set opacity(val) {
            this.style.opacity = val;
            this.getScene().nextRender();
        },
        get linewidth() {
            return this.style.linewidth;
        },
        set linewidth(val) {
            this.style.linewidth = val;
            this.getScene().nextRender();
        },
        get linedash() {
            return this.style.linedash;
        },
        set linedash(val) {
            this.style.linedash = val;
            this.getScene().nextRender();
        },
        get linedashoffset() {
            return this.style.linedashoffset;
        },
        set linedashoffset(val) {
            this.style.linedashoffset = val;
            this.getScene().nextRender();
        },
        get linecap() {
            return this.style.linecap;
        },
        set linecap(val) {
            this.style.linecap = val;
            this.getScene().nextRender();
        },
        get linejoin() {
            return this.style.linejoin;
        },
        set linejoin(val) {
            this.style.linejoin = val;
            this.getScene().nextRender();
        },
        arc: function (x1, y1, x2, y2) {
            var sign = x1 * y2 - x2 * y1;
            var temp = (x1 * x2 + y1 * y2) / Math.sqrt(x1 * x1 + y1 * y1) * Math.sqrt(x2 * x2 + y2 * y2);
            if (temp < -1) temp = -1;
            if (temp > 1) temp = 1;
            var n = Math.acos(temp);
            if (sign > 0) return n;
            else return -n;
        },
        createPath: function (ctx, ratio) {
            var tempAction = this.action, tempPoint = this.points;
            ratio = ratio || 1;
            ctx.beginPath();
            var index = 0;
            for (var i = 0, len = tempAction.length; i < len; i++) {
                if (tempAction[i] === "M") {
                    var p0 = tempPoint[index++], p1 = tempPoint[index++];
                    ctx.moveTo(p0 * ratio, p1 * ratio, 0);
                } else if (tempAction[i] === "L" || tempAction[i] === "H" || tempAction[i] === "V") {
                    var p0 = tempPoint[index++], p1 = tempPoint[index++];
                    ctx.lineTo(p0 * ratio, p1 * ratio);
                } else if (tempAction[i] === "C" || tempAction[i] === "S") {
                    var p0 = tempPoint[index++], p1 = tempPoint[index++], p2 = tempPoint[index++], p3 = tempPoint[index++], p4 = tempPoint[index++], p5 = tempPoint[index++];
                    ctx.bezierCurveTo(p0 * ratio, p1 * ratio, p2 * ratio, p3 * ratio, p4 * ratio, p5 * ratio);
                } else if (tempAction[i] === "Z") {
                    ctx.closePath();
                } else if (tempAction[i] === "Q" || tempAction[i] === "T") {
                    var p0 = tempPoint[index++], p1 = tempPoint[index++], p2 = tempPoint[index++], p3 = tempPoint[index++];
                    ctx.quadraticCurveTo(p0 * ratio, p1 * ratio, p2 * ratio, p3 * ratio);
                } else if (tempAction[i] === "A") {
                    var p0 = tempPoint[index++], p1 = tempPoint[index++], p2 = tempPoint[index++], 
                        p3 = tempPoint[index++], p4 = tempPoint[index++], p5 = tempPoint[index++],
                        p6 = tempPoint[index++], p7 = tempPoint[index++], p8 = tempPoint[index++];
                    var theta = p4,
                        fa = p5 * ratio,
                        fs = p6 * ratio,
                        rx = p2 * ratio,
                        ry = p3 * ratio,
                        x2 = p7 * ratio,
                        y2 = p8 * ratio,
                        x1 = p0 * ratio,
                        y1 = p1 * ratio;

                    var cos = Math.cos(theta),
                        sin = Math.sin(theta);
                    var nx1 = cos * (x1 - x2) / 2 + sin * (y1 - y2) / 2,
                        ny1 = -sin * (x1 - x2) / 2 + cos * (y1 - y2) / 2;
                    var tempNumber = (rx * rx * ry * ry - rx * rx * ny1 * ny1 - ry * ry * nx1 * nx1);
                    tempNumber = tempNumber < 0 ? 0 : tempNumber;
                    var tempN = Math.sqrt(
                        tempNumber /
                        (rx * rx * ny1 * ny1 + ry * ry * nx1 * nx1)
                    );

                    if (fa !== fs) {
                        tempN = -tempN;
                    }
                    var ncx = tempN * rx * ny1 / ry,
                        ncy = -tempN * ry * nx1 / rx;

                    var cx = cos * ncx - sin * ncy + (x1 + x2) / 2,
                        cy = sin * ncx + cos * ncy + (y1 + y2) / 2;

                    var t1 = this.arc(1, 0, (nx1 - ncx) / rx, (ny1 - ncy) / ry);
                    var dt = this.arc((nx1 - ncx) / rx, (ny1 - ncy) / ry, (-nx1 - ncx) / rx, (-ny1 - ncy) / ry);
                    var t2 = dt + t1;
                    if (fs) {
                        ctx.ellipse(cx, cy, rx, ry, theta, t1, t2, true);
                    } else {
                        ctx.ellipse(cx, cy, rx, ry, theta, t1, t2, false);
                    }
                }
            }
        },
    })
})